package com.zkh.myframe.database.mapper.proxy;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

import com.zkh.myframe.core.FrameUtils;
import com.zkh.myframe.database.DataTemplate;
import com.zkh.myframe.database.mapper.MapperEntity;
import com.zkh.myframe.database.mapper.MapperException;
import com.zkh.myframe.database.mapper.MapperPageRequest;
import com.zkh.myframe.database.mapper.SQLEntity;
import com.zkh.myutils.bean.Type;
import com.zkh.myutils.database.conutils.PoolUtils;
import com.zkh.myutils.database.data.handler.ArrayListHandler;
import com.zkh.myutils.database.data.handler.BeanListHandler;
import com.zkh.myutils.database.data.handler.ColumnListHandler;
import com.zkh.myutils.database.data.handler.Handler;
import com.zkh.myutils.database.data.handler.MapListHandler;
import com.zkh.myutils.database.data.handler.ObjectHandler;
import com.zkh.myutils.database.dbpool.ConnectConfigure;
import com.zkh.myutils.database.dbpool.DBType;
import com.zkh.myutils.page.Pagination;
import com.zkh.myutils.proxy.MethodHandler;
import com.zkh.myutils.proxy.ProxyMethod;
import com.zkh.myutils.utils.StringUtils;

/**
 * Mapper代理对象
 * @author zkh
 */
public class MapperProxy implements MethodHandler {
	private static final String ARRAY = "java.lang.Object[]";
	private static final String MAP = "java.util.Map";
	
	/**
	 * 单字段查询
	 */
	private static final List<String> singleTypeList = Arrays.asList(
		Type.BIGDECIMAL, Type.BIGINTEGER, Type.LONG, Type.INTEGER, Type.SHORT, Type.BYTE, Type.BOOL,
		Type.FLOAT, Type.DOUBLE, Type.STRING, Type.DATE
	);
	
	@Override
	public Object invoke(Object proxy, ProxyMethod proxyMethod) throws Throwable {
		//参数容器
		Map<String, Object> paramMap = new HashMap<>();
		//分页对象，不为null表示分页
		MapperPageRequest[] mapperPageRequest = new MapperPageRequest[1];
		//遍历参数
		proxyMethod.forEachParam(paramReader->{
			//未分页，则检查是否分页
			if(mapperPageRequest[0]==null) {
				//是否实现MapperPageRequest接口
				boolean selectByPage = MapperPageRequest.class.isAssignableFrom(paramReader.getType());
				//分页，但是为空
				if(selectByPage) {
					if(paramReader.getParamValue()==null) {
						throw new MapperException("分页参数对象"+paramReader.getType().getName()+"不能为null");
					}else {
						mapperPageRequest[0] = (MapperPageRequest) paramReader.getParamValue();
					}
				}
			}
			//实例化参数对象（默认名+参数原名）
			paramMap.put("arg" + paramReader.getIndex(), paramReader.getParamValue());
			paramMap.put(paramReader.getParamName(), paramReader.getParamValue());
		});
		/*******  执行并返回数据 *******/
		return execute(proxyMethod.getSuperClassName(), proxyMethod.getSuperName(), proxyMethod.getMethod().getGenericReturnType(), paramMap, mapperPageRequest[0]);
	}
	
	/**
	 * 执行并返回数据
	 * @param namespace 命名空间
	 * @param id ID
	 * @param rtnType 返回值类型(无返回值为void)
	 */
	private Object execute(String namespace, String id, java.lang.reflect.Type rtnType, Map<String, Object> paramMap, MapperPageRequest mapperPageRequest) {
		//返回值
		Object rtnValue;
		//获取Mapper实例
		SQLEntity sqlEntity = MapperEntity.getOperateSQLEntity(namespace, id);
		//为空
		if(sqlEntity==null) {
			throw new MapperException("未配置" + namespace + "." + id + "方法的Mapper映射");
		}
		//获取SQL
		String sql = sqlEntity.getSql(paramMap);
		//查询类型
		String sqltype = sqlEntity.getOperType();
		//数据源
		String dataSource = sqlEntity.getDataSource();
		//参数
		Object[] params = sqlEntity.getParams();
		//DataTemplate对象
		DataTemplate template = FrameUtils.getResourceClass(DataTemplate.class);
		//有指定数据源
		if(StringUtils.isNotEmpty(dataSource)) {
			template.setDataSource(dataSource);
		}
		//查询
		if("select".equals(sqltype)) {
			rtnValue = query(template, sql, params, rtnType, mapperPageRequest, ()->{
				//查询count
				SQLEntity countEntity = MapperEntity.getOperateSQLEntity(namespace, id + "_COUNT");
				//有值
				if(countEntity!=null) {
					//获取SQL和查询参数
					String countSql = countEntity.getSql(paramMap);
					Object[] countParams = countEntity.getParams();
					//执行查询
					return template.doQuery(countSql, new ObjectHandler<>(Long.class), countParams);
				}
				return null;
			});
		}else {//增删改
			//返回类型
			String type = rtnType.getTypeName();
			//执行
			int res = template.doExecute(PoolUtils.getTransactionConnection(), sql, params);
			//返回值
			if(Type.BASE_INT.equals(type) || Type.INTEGER.equals(type)) {
				rtnValue = res;
			}else if(Type.BASE_BOOL.equals(type) || Type.BOOL.equals(type)) {
				return true;
			}else if(Type.VOID.equals(type)){
				rtnValue = null;
			} else {
				throw new MapperException(sqltype.toUpperCase() + "操作只能返回整型和boolean类型");
			}
		}
		//返回值
		return rtnValue;
	}
	
	/**
	 * 执行Select语句并返回对应数据
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private Object query(DataTemplate template, String sql, Object[] params, java.lang.reflect.Type rtnType, MapperPageRequest mapperPageRequest, Supplier<Long> countGetter) {
		try {
			//获取返回类型
			String rtnTypeName = rtnType.getTypeName();
			//获取泛型开始位置
			int gs = rtnTypeName.indexOf('<');
			//是否分页
			if(mapperPageRequest!=null) {
				//真实响应类型
				Class respType = Class.forName(gs==-1 ? rtnTypeName : rtnTypeName.substring(0, gs));
				//默认实体为Map
				Class<?> resType = Map.class;
				//检查返回类型必须是Pagination或其子类
				if(!Pagination.class.isAssignableFrom(respType)) {
					throw new MapperException("分页查询必须返回Pagination或其子类");
				}
				//获取泛型类型
				if(gs>-1) {
					//如果内部还有泛型，直接过滤掉
					int gss = rtnTypeName.indexOf('<', gs+1);
					//截取类型
					String gType = rtnTypeName.substring(gs+1, gss>-1 ? gss : rtnTypeName.length()-1);
					//数组
					if(ARRAY.equals(gType)) {
						resType = Object[].class;
					}//实体
					else if(!StringUtils.isExist(gType, "?", MAP)) {
						resType = Class.forName(gType);
					}
				}
				//获取总记录数
				Long totalItems = countGetter.get();
				//添加分页
				template.addPage(mapperPageRequest.getPageIndex(), mapperPageRequest.getPageItem());
				//分页查询
				Pagination page = totalItems==null ? template.doSelectPage(resType, sql, params) : template.doSelectPage(resType, totalItems, sql, params);
				//返回对象
				Pagination response = (Pagination) respType.newInstance();
				//分页信息
				response.setTotalItem(page.getTotalItem());
				response.setTotalPage(page.getTotalPage());
				response.setPageIndex(page.getPageIndex());
				response.setPageItem(page.getPageItem());
				//直接保存
				response.setDataList(page.getDataList());
				//返回对象
				return response;
			}else {
				//泛型类型
				String gType = null;
				//获取泛型类型
				if(gs>-1) {
					//如果内部还有泛型，直接过滤掉
					int gss = rtnTypeName.indexOf('<', gs+1);
					//截取类型（java.lang.Object[]，"?"，MAP）
					gType = rtnTypeName.substring(gs+1, gss>-1 ? gss : rtnTypeName.length()-1);
					//实际类型
					rtnTypeName = rtnTypeName.substring(0, gs);
				}
				//真实响应类型
				Class respType = ARRAY.equals(rtnTypeName) ? Object[].class : Class.forName(rtnTypeName);
				//多记录查询
				boolean multiQuery = List.class.isAssignableFrom(respType);
				//单记录查询
				if(!multiQuery) {
					//库类型
					DBType dbtype = ConnectConfigure.getDefaultSourceConfig().getDbtype();
					//MySQL
					if(DBType.TYPE_MYSQL.equals(dbtype)) {
						sql = "select * from (" + sql + ") t limit 1";
					}else if(DBType.TYPE_ORACLE.equals(dbtype)) {
						sql = "select * from (" + sql + ") t where rownum=1";
					}else if(DBType.TYPE_SQLSERVER.equals(dbtype)) {
						sql = "select top 1" + sql.substring(6);
					}
				}
				//数组处理handler
				Handler handler;
				//数组
				if(StringUtils.isExist(ARRAY, gType, rtnTypeName)) {
					handler = new ArrayListHandler();
				}//Map
				else if(StringUtils.isExist(MAP, gType, rtnTypeName)) {
					handler = new MapListHandler();
				}//单字段查询
				else if(singleTypeList.contains(gType) || singleTypeList.contains(rtnTypeName)) {
					handler = new ColumnListHandler<Object>(Class.forName(multiQuery ? gType : rtnTypeName));
				}//其它默认实体查询
				else {
					handler = new BeanListHandler<>(Class.forName(multiQuery ? gType : rtnTypeName));
				}
				//执行查询
				List list = (List) template.doQuery(PoolUtils.getTransactionConnection(), sql, handler, params);
				//单记录查询
				if(!multiQuery) {
					return list.isEmpty() ? null : list.get(0);
				}
				//响应容器
				List container = respType.isInterface() ? new ArrayList() : (List) respType.newInstance();
				//添加
				container.addAll(list);
				//返回
				return container;
			}
		}catch(ClassNotFoundException e) {
			throw new MapperException("结果集转换失败", e);
		}catch (InstantiationException | IllegalAccessException e) {
			throw new MapperException("结果对象实例化失败", e);
		}
	}
}
